/*!
 * @author  Vladimir Kerimov
 * @file    time.h
 * @version 1.0
 * @brief   Time with date and time delta
 * @date    2013
 */

/*
 *    ______   __
 *   /  ___/  /__/   __  ___      _____        _____     __    __     _____        __  ___      _____     __  ___
 *  |  |___    __   /  |/   \    / __  \      /  _  \   |  |  |  |   /  _  \      |  |/   \    /  _  \   |  |/   \
 *   \___  \  /  |  |   __   |  | /  __/     |  / \  |  |  |  |  |  |  / \  |     |   __   |  |  / \  |  |   __   |
 *  ____/  /  |  |  |  /  \  |  |   /___     |  \_/  |  |   \_|  |  |  \_/  |     |  /  \  |  |  \_/  |  |  /  \  |
 * /______/   |__/  |__|  |__/   \_____/      \___   |   \___/|__|   \___/|_|     |__|  |__|   \_____/   |__|  |__|
 *                                                |  |
 *                                                |__/
 *
 */

#pragma once

#include <qua/api.h>
#include <qua/lazy.h>
#include <stdint.h>

namespace qua
{
    class QUAZAR_API time
    {
    public:
        class delta;

        time();

        time( int year, int month, int day, int hour=0, int minute=0, int second=0, int microsecond=0 );

        int year()  const;
        int month() const;
        int day()   const;
        int hour()  const;
        int minute() const;
        int second() const;
        int millisecond() const;
        int microsecond() const;

        static time now();
        static time min();
        static time max();
        static time pos_inf();
        static time neg_inf();
        static time not_a_time();

        bool is_inf() const;
        bool is_pos_inf() const;
        bool is_neg_inf() const;
        bool is_not_a_time() const;

        delta time_of_day() const;

        delta operator - ( time const& other ) const;

        time operator + ( delta const& other ) const;

        time& operator += ( delta const& other );

        bool operator == ( time const& other ) const;
        bool operator != ( time const& other ) const;
        bool operator <= ( time const& other ) const;
        bool operator >= ( time const& other ) const;
        bool operator <  ( time const& other ) const;
        bool operator >  ( time const& other ) const;

    private:
        class data;
        
        lazy<data> m_data;

        time( data* data_ptr );
    };

    class QUAZAR_API time::delta
    {
    public:
        class value;

        delta();

        delta( delta&& temp );

        delta( value const& alfa );
        delta( value const& alfa, value const& beta );
        delta( value const& alfa, value const& beta, value const& gamma );
        delta( value const& alfa, value const& beta, value const& gamma, value const& epsilon );
        delta( value const& alfa, value const& beta, value const& gamma, value const& epsilon, value const& zeta );
        delta( value const& alfa, value const& beta, value const& gamma, value const& epsilon, value const& zeta, value const& eta );

        int64_t in_days() const;
        int64_t in_hours() const;
        int64_t in_minutes() const;
        int64_t in_seconds() const;
        int64_t in_milliseconds() const;
        int64_t in_microseconds() const;

        int hour() const;
        int minute() const;
        int second() const;
        int millisecond() const;
        int microsecond() const;

        delta& operator = ( value const& alfa );
        delta& operator = ( delta&& temp );

        delta operator + ( delta const& other ) const;
        delta operator - ( delta const& other ) const;

        delta& operator += ( delta const& other );
        delta& operator -= ( delta const& other );

        bool operator == ( delta const& other ) const;
        bool operator != ( delta const& other ) const;
        bool operator <= ( delta const& other ) const;
        bool operator >= ( delta const& other ) const;
        bool operator <  ( delta const& other ) const;
        bool operator >  ( delta const& other ) const;

    private:
        friend class time;

        class data;
        lazy<data> m_data;

        delta( data* data_ptr );
        delta( data&& temp );
    };

    class time::delta::value
    {
    public:
        enum type
        {
            of_days,
            of_hours,
            of_minutes,
            of_seconds,
            of_milliseconds,
            of_microseconds,
            of_unknown_type
        };

        value( int64_t delta, type tag )
            : m_delta( delta ), m_type( tag ) {
        }

        operator int64_t () const {
            return m_delta;
        }

        type get_type() const {
            return m_type;
        }

    private:
        int64_t m_delta;
        type m_type;
    };

    class days : public time::delta::value
    {
    public:
        days( int64_t delta )
            : time::delta::value( delta, of_days ) {
        }
    };

    class hours : public time::delta::value
    {
    public:
        hours( int64_t delta )
            : time::delta::value( delta, of_hours ) {
        }
    };

    class minutes : public time::delta::value
    {
    public:
        minutes( int64_t delta )
            : time::delta::value( delta, of_minutes ) {
        }
    };

    class seconds : public time::delta::value
    {
    public:
        seconds( int64_t delta )
            : time::delta::value( delta, of_seconds ) {
        }
    };

    class milliseconds : public time::delta::value
    {
    public:
        milliseconds( int64_t delta )
            : time::delta::value( delta, of_milliseconds ) {
        }
    };

    class microseconds : public time::delta::value
    {
    public:
        microseconds( int64_t delta )
            : time::delta::value( delta, of_microseconds ) {
        }
    };
}
